﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ImageTagToolViewModel.Others;
using ImageTagToolViewModel.Interfaces;

namespace ImageTagToolViewModel.Models
{
    /// <summary>
    /// Managing tags in runtime.
    /// </summary>
    public class TagDatabase : IMemoryUsageApproximable
    {
        /// <summary>
        /// The key of the scene tags in the TagDatabase.
        /// </summary>
        public const string SceneTags = "scene tags";
        /// <summary>
        /// The key of the person tags in the TagDatabase.
        /// </summary>
        public const string PersonTags = "person tags";
        /// <summary>
        /// The key of the other tags in the TagDatabase.
        /// </summary>
        public const string OtherTags = "other tags";
        /// <summary>
        /// This key indicates, that the selected key is 
        /// </summary>
        public const string InvalidTagKey = "invalid";

        private static TagDatabase _Instance;
        public static TagDatabase Instance
        {
            get
            {
                if (_Instance == null)
                {
                    object locker = new object();
                    lock (locker)
                    {
                        if (_Instance == null)
                            _Instance = new TagDatabase();
                    }
                }
                return _Instance;
            }
        }

        private Dictionary<string, ListWithReferenceCounter<string>> _Tags;

        private TagDatabase()
        {
            _Tags = new Dictionary<string, ListWithReferenceCounter<string>>();
        }

        /// <summary>
        /// Gets the tag's list of the specified tag-group.
        /// </summary>
        public ListWithReferenceCounter<string> GetCollection(string key)
        {
            // Validating the given key.
            if (!(key == SceneTags || key == PersonTags || key == OtherTags))
                throw new KeyNotFoundException("The given key is not valid: " + key);

            // Checking the dictionary if the key is not present.
            if (!_Tags.ContainsKey(key))
                _Tags[key] = new ListWithReferenceCounter<string>();

            return _Tags[key];
        }

        /// <summary>
        /// Tries to approximate the stored data in this TagDatabase object.
        /// </summary>
        /// <returns>The amount of data in Bytes</returns>
        public int ApproximateMemoryUsage()
        {
            int memory = 0;
            foreach (var key in _Tags.Keys)
            {
                memory += Encoding.UTF8.GetByteCount(key);
                foreach (var entry in _Tags[key].Entries)
                {
                    memory += Encoding.UTF8.GetByteCount(entry);
                    memory += 4; // The int32 reference counter.
                }
            }
            return memory;
        }

        /// <summary>
        /// Restores database after deserialization.
        /// </summary>
        public void Restore(List<Picture> pictures)
        {
            var scenes = GetCollection(SceneTags);
            var persons = GetCollection(PersonTags);
            var others = GetCollection(OtherTags);
            foreach (var p in pictures)
            {
                p.SceneTags.ForEach(t => scenes.Add(t));
                p.PersonTags.ForEach(t => persons.Add(t));
                p.OtherTags.ForEach(t => others.Add(t));
            }
        }

        /// <summary>
        /// Clears all data within this database.
        /// </summary>
        public void Clear()
        {
            _Tags.Clear();
        }
    }
}
